package com.jccfc.iMessage.impl;

import com.jccfc.common.HttpClientUtil;
import com.jccfc.common.MessageUtil;
import com.jccfc.domain.sms.*;
import com.jccfc.enums.MessageChannelEnum;
import com.jccfc.enums.MessageRtnCodeEnum;
import com.jccfc.enums.MessageStatusEnum;
import com.jccfc.iMessage.IMessage;
import com.jccfc.iMessageFactory.ParentMessageFactory;
import com.jccfc.iMessageTo.IMessageTo;
import com.jccfc.iMessageTo.impl.MobileMessageTo;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;

import java.math.BigDecimal;
import java.sql.DriverManager;

import org.apache.commons.httpclient.util.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Auther: qym
 * @Date: 2020/12/28 14:02
 * @Description: 发送手机消息的对象类
 */
public class MobileMessage implements IMessage {

    //日志打印
    private Logger logger = LoggerFactory.getLogger(MobileMessage.class);

    //通用短信模板的变量名
    private String comSmsTemplateVar = null;
    //通用短信模板Id
    private String comSmsTemplateId = null;
    //url
    private String url = null;
    //app名
    private String appCode = null;
    //批次号
    private String batchNo = null;
    //密钥
    private String secretKey = null;
    //mysql驱动
    private String mysqlDriver = null;
    //mysql url
    private String mysqlUrl = null;
    //mysql username
    private String mysqlUsername = null;
    //mysql password
    private String mysqlPassword = null;

    //初始化消息发送需要的参数
    public MobileMessage() {
        //初始化参数
        init();
    }

    /**
     * @Auther: qym
     * @Description: 发送一条短信信息  http方式方式短信信息不进行落库
     * @Param: 发送的消息对象
     * @Return: 消息发送的状态
     * @Date: 2020/12/28 17:32
     */
    @Override
    public boolean sendMessage(IMessageTo iMessageTo) {
        //对数据进行校验
        boolean pass = MessageUtil.isPass(iMessageTo, MobileMessageTo.class);
        if (!pass) {
            logger.error("消息发送失败,传入参数类型不正确或者为null");
            return false;
        }
        //获取传入数据的具体类型
        MobileMessageTo mobileMessageTo = MessageUtil.downcast(iMessageTo, MobileMessageTo.class);
        //构造短信发送体
        List<SmsReqSmsBodyEntity> smsReqParamEntityList = new ArrayList<>();
        smsReqParamEntityList.add(getSmsReqSmsBodyEntity(mobileMessageTo));
        //发送短信
        SmsRepBodyEntity smsRepBodyEntity = sendMobileMessage(smsReqParamEntityList);
        //检验是否发送成功
        if (smsRepBodyEntity == null) {
            logger.error("调用短信服务失败, 流水号RequestSN为:{}", mobileMessageTo.getRequestSN());
            return false;
        } else if (!("0000".equals(smsRepBodyEntity.getCode()) && smsRepBodyEntity.isState())) {
            logger.error("调用短信服务失败, 流水号RequestSN为:{},state:{},code:{},message:{},data:{}",
                    mobileMessageTo.getRequestSN(), smsRepBodyEntity.isState(), smsRepBodyEntity.getCode(), smsRepBodyEntity.getMessage(), smsRepBodyEntity.getData());
            return false;
        }
        logger.info("调用短信服务成功!");
        return true;
    }

    /**
     * @Auther: qym
     * @Description: 发送多条短信信息  http方式方式短信信息不进行落库
     * @Param: 消息发送数据
     * @Return: 消息发送状态
     * @Date: 2020/12/31 14:34
     */
    @Override
    public boolean sendMessage(List<IMessageTo> iMessageTos) {
        //对数据进行校验
        boolean pass = MessageUtil.isPass(iMessageTos, MobileMessageTo.class);
        if (!pass) {
            logger.error("消息发送失败,传入参数类型不正确或者为null");
            return false;
        }
        List<MobileMessageTo> mobileMessageTos = MessageUtil.downcast(iMessageTos, MobileMessageTo.class);
        //封装消息体
        List<SmsReqSmsBodyEntity> smsReqParamEntityList = new ArrayList<>();
        for (MobileMessageTo mmt : mobileMessageTos) {
            smsReqParamEntityList.add(getSmsReqSmsBodyEntity(mmt));
        }
        //发送短信
        SmsRepBodyEntity smsRepBodyEntity = sendMobileMessage(smsReqParamEntityList);
        //检验是否发送成功
        if (smsRepBodyEntity == null) {
            logger.error("调用短信服务失败, 流水号RequestSN为:{}", mobileMessageTos.get(0).getRequestSN());
            return false;
        } else if (!("0000".equals(smsRepBodyEntity.getCode()) && smsRepBodyEntity.isState())) {
            logger.error("调用短信服务失败, 流水号RequestSN为:{},state:{},code:{},message:{},data:{}",
                    mobileMessageTos.get(0).getRequestSN(), smsRepBodyEntity.isState(), smsRepBodyEntity.getCode(), smsRepBodyEntity.getMessage(), smsRepBodyEntity.getData());
            return false;
        }
        logger.info("调用短信服务成功!");
        return true;
    }

    /**
     * @Auther: qym
     * @Description: 一条数据入库 定时任务执行的短信内容添加到数据库
     * @Param:
     * @Return:
     * @Date: 2020/12/31 14:35
     */
    @Override
    public boolean sendScheduleMessage(IMessageTo iMessageTo) {
        //对数据进行校验
        boolean pass = MessageUtil.isPass(iMessageTo, MobileMessageTo.class);
        //校验不通过日志打印
        if (!pass) {
            logger.error("消息发送失败,传入参数类型不正确或者为null");
            return false;
        }
        MobileMessageTo mobileMessageTo = MessageUtil.downcast(iMessageTo, MobileMessageTo.class);
        //防止重复提交 检查流水号
        boolean requestSN = isRequestSN(mobileMessageTo.getRequestSN());
        if (requestSN) {
            logger.error("消息发送失败,流水号重复。流水号RequestSN为:{}", mobileMessageTo.getRequestSN());
            return false;
        }
        //获取batchId
        String batchId = getBatchId();
        if (batchId == null) {
            logger.error("消息发送失败,batchId获取失败。流水号RequestSN为:{},batchId:{}", mobileMessageTo.getRequestSN(), batchId);
            return false;
        }
        //新建交易表状态
        String smsTradeStatus = insertSmsTrade(batchId, mobileMessageTo, 1);
        if (smsTradeStatus == null) {
            logger.error("消息发送失败,交易表状态插入数据失败。流水号RequestSN为:{},batchId:{}", mobileMessageTo.getRequestSN(), batchId);
            return false;
        }
        //执行短信发送操作
        List<MobileMessageTo> mobileMessageTos = new ArrayList<>();
        mobileMessageTos.add(mobileMessageTo);
        SmsScheduleResult smsScheduleResult = sendSmsProcess(batchId, mobileMessageTos);
        if (smsScheduleResult == null) {
            logger.error("消息发送失败,定时任务表短信数据插入失败。流水号RequestSN为:{},batchId:{}", mobileMessageTo.getRequestSN(), batchId);
            return false;
        }
        //更新交易表状态
        String messageStatusRtnCode = updateSmsTradeStatus(batchId, smsScheduleResult);
        if (messageStatusRtnCode == null) {
            logger.error("消息发送失败,更新交易表状态失败。流水号RequestSN为:{},batchId:{}", mobileMessageTo.getRequestSN(), batchId);
            return false;
        }
        logger.info("调用短信服务成功!");
        return true;
    }

    /**
     * @Auther: qym
     * @Description: 批量数据入库 定时任务执行的短信内容添加到数据库
     * @Param:
     * @Return:
     * @Date: 2020/12/31 14:35
     */
    @Override
    public boolean sendScheduleMessage(List<IMessageTo> iMessageTos) {
        //对数据进行校验
        boolean pass = MessageUtil.isPass(iMessageTos, MobileMessageTo.class);
        //校验不通过日志打印
        if (!pass) {
            logger.error("消息发送失败,传入参数类型不正确或者为null");
            return false;
        }
        List<MobileMessageTo> mobileMessageTos = MessageUtil.downcast(iMessageTos, MobileMessageTo.class);
        //防止重复提交 检查流水号 list集合内所有iMessageTos应该有相同的流水号
        boolean requestSN = isRequestSN(mobileMessageTos.get(0).getRequestSN());
        if (requestSN) {
            logger.error("消息发送失败,流水号重复。流水号RequestSN为:{}", mobileMessageTos.get(0).getRequestSN());
            return false;
        }
        //获取batchId
        String batchId = getBatchId();
        if (batchId == null) {
            logger.error("消息发送失败,batchId获取失败。流水号RequestSN为:{},batchId:{}", mobileMessageTos.get(0).getRequestSN(), batchId);
            return false;
        }
        //新建交易表状态
        String smsTradeStatus = insertSmsTrade(batchId, mobileMessageTos.get(0), mobileMessageTos.size());
        if (smsTradeStatus == null) {
            logger.error("消息发送失败,交易表状态插入数据失败。流水号RequestSN为:{},batchId:{}", mobileMessageTos.get(0).getRequestSN(), batchId);
            return false;
        }
        //执行短信发送操作
        SmsScheduleResult smsScheduleResult = sendSmsProcess(batchId, mobileMessageTos);
        if (smsScheduleResult == null) {
            logger.error("消息发送失败,定时任务表短信数据插入失败。流水号RequestSN为:{},batchId:{}", mobileMessageTos.get(0).getRequestSN(), batchId);
            return false;
        }
        //更新交易表状态
        String messageStatusRtnCode = updateSmsTradeStatus(batchId, smsScheduleResult);
        if (messageStatusRtnCode == null) {
            logger.error("消息发送失败,更新交易表状态失败。流水号RequestSN为:{},batchId:{}", mobileMessageTos.get(0).getRequestSN(), batchId);
            return false;
        }
        logger.info("调用短信服务成功!");
        return true;
    }

    /**
     * @Auther: qym
     * @Description: 构造短信体
     * @Param:
     * @Return:
     * @Date: 2021/1/4 10:59
     */
    private SmsReqSmsBodyEntity getSmsReqSmsBodyEntity(MobileMessageTo mobileMessageTo) {
        Map<String, String> smsContentMap = new HashMap<>();
        smsContentMap.put(comSmsTemplateVar, mobileMessageTo.getSmsContent());
        SmsReqSmsBodyEntity smsReqSmsBodyEntity = new SmsReqSmsBodyEntity(mobileMessageTo.getMobileNumber(), comSmsTemplateId, smsContentMap);
        return smsReqSmsBodyEntity;
    }

    /**
     * @Auther: qym
     * @Description: 发送短信消息
     * @Param: 需要发送的短信信息
     * @Return: 发送短信状态
     * @Date: 2021/1/4 14:44
     */
    private SmsRepBodyEntity sendMobileMessage(List<SmsReqSmsBodyEntity> smsReqParamEntityList) {
        SmsRepBodyEntity smsRepBodyEntity = HttpClientUtil.smsHttpPostSendJson(url,
                new SmsReqBodyEntity(new SmsReqHeaderEntity(appCode, batchNo, secretKey), smsReqParamEntityList),
                SmsRepBodyEntity.class,
                null);
        return smsRepBodyEntity;
    }

    /**
     * @Auther: qym
     * @Description: 获取jdbc连接
     * @Param:
     * @Return:
     * @Date: 2021/1/5 9:51
     */
    private Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName(mysqlDriver);
            connection = (Connection) DriverManager.getConnection(mysqlUrl, mysqlUsername, mysqlPassword);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * @Auther: qym
     * @Description: 关闭数据库连接
     * @Param:
     * @Return:
     * @Date: 2021/1/6 15:56
     */
    private void closeConnection(ResultSet resultSet, PreparedStatement ps, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (ps != null) {
                ps.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (Exception e) {
            logger.error("关闭数据库连接发生异常:{}", e);
        }
    }

    /**
     * @Auther: qym
     * @Description: 校验流水号是否已经存在，防止重复提交
     * @Param: 流水号
     * @Return: 流水号是否存
     * @Date: 2021/1/6 11:45
     */
    private boolean isRequestSN(String requestSN) {
        //数据库连接
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        try {
            //获取jdbc连接
            connection = getConnection();
            //jc_sms_trade交易信息TRADE
            String selectNextVal = "select BATCH_ID from jc_sms_trade where REQUEST_SN = ?";
            ps = (PreparedStatement) connection.prepareStatement(selectNextVal);
            ps.setString(1, requestSN);
            resultSet = ps.executeQuery();
            //查询系统表 看是否存在相同的requestSN
            return resultSet.next();
        } catch (Exception e) {
            logger.error("流水号查询异常:{}", e);
        } finally {
            closeConnection(resultSet, ps, connection);
        }
        return false;
    }

    /**
     * @Auther: qym
     * @Description: 获取batchId 从公共的自增主键表 获取一个自增主键
     * @Param:
     * @Return:
     * @Date: 2021/1/5 11:37
     */
    private String getBatchId() {
        //获取当前日期
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
        String currentDay = format.format(new Date());
        //数据库连接
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        String nextval = "";
        try {
            //获取jdbc连接
            connection = getConnection();
            //jc_sms_trade交易信息TRADE
            String selectNextVal = "values nextval for SEQ_BAT_MSGRPT";
            ps = (PreparedStatement) connection.prepareStatement(selectNextVal);
            resultSet = ps.executeQuery();
            //查询系统表 结果只有一个值
            nextval = resultSet.getString(1);
        } catch (Exception e) {
            logger.error("nextval查询异常:{}", e);
            return null;
        } finally {
            closeConnection(resultSet, ps, connection);
        }
        //返回batchId
        return StringUtils.join(new String[]{currentDay, String.format("%07d", new Object[]{Long.valueOf(nextval)})}).toString();
    }

    /**
     * @Auther: qym
     * @Description: 插入交易信息
     * @Param: 需要插入的数据
     * @Return: branchId
     * @Date: 2021/1/5 14:35
     */
    private String insertSmsTrade(String batchId, MobileMessageTo mobileMessageTo, int size) {
        //连接实例
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //获取jdbc连接
            connection = getConnection();
            //关闭自动事物提交
            connection.setAutoCommit(false);
            //jc_sms_trade交易信息TRADE
            String insertSql = "insert into jc_sms_trade (BATCH_ID, SMS_CHANNEL, REQUEST_SN,SYS_ID, SEND_DATE, SEND_TIME,SEND_COUNT, STATUS) " +
                    " values (?,?,?,?,?,?,?,?)";
            ps = (PreparedStatement) connection.prepareStatement(insertSql);
            //填充插入参数
            ps.setString(1, batchId);
            ps.setString(2, MessageChannelEnum.CBS.getChannel());
            ps.setString(3, mobileMessageTo.getRequestSN());
            ps.setString(4, mobileMessageTo.getSysId());
            Date date = new Date();
            String sendDate = DateUtil.formatDate(date, "yyyyMMdd");
            String sendTime = DateUtil.formatDate(date, "HHmmss");
            ps.setString(5, sendDate);
            ps.setString(6, sendTime);
            ps.setInt(7, size);
            ps.setString(8, "00");
            //执行插入jc_sms_trade sql
            ps.executeUpdate();
            connection.commit();
            logger.info("提交完成");
            connection.setAutoCommit(true);
        } catch (Exception e) {
            try {
                //发生异常回滚
                if (!connection.isClosed()) {
                    connection.rollback();
                    connection.setAutoCommit(true);
                }
            } catch (Exception e1) {
                logger.error("回滚事物发送异常:{}", e);
            }
            return null;
        } finally {
            closeConnection(null, ps, connection);
        }
        return MessageRtnCodeEnum.SUCCESS.getRtnCode();
    }

    /**
     * @Auther: qym
     * @Description: 执行短信发送操作
     * @Param: 批次号, 发送数据
     * @Return: smsStatus 发送状态
     * @Date: 2021/1/5 15:22
     */
    private SmsScheduleResult sendSmsProcess(String batchId, List<MobileMessageTo> mobileMessageTos) {
        //定时消息入库结果
        SmsScheduleResult smsScheduleResult = new SmsScheduleResult();
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //获取jdbc连接
            connection = getConnection();
            //关闭自动事物提交
            connection.setAutoCommit(false);
            String sendSmsProcessSql = "insert into tbl_bat_msgrpt(mr_seq, mr_cust_no, mr_cust_name, mr_msg_type,mr_date, mr_amt, mr_mobile_no, mr_remain_amt,mr_send_ind, mr_message, mr_user_field3) " +
                    " values (?,?,?,?,?,?,?,?,?,?,?)";
            ps = (PreparedStatement) connection.prepareStatement(sendSmsProcessSql);
            for (int i = 0; i < mobileMessageTos.size(); i++) {
                MobileMessageTo mobileMessageTo = mobileMessageTos.get(i);
                //插入数据填充
                String smsSeq = String.format("%05d", i);
                String mrSeq = StringUtils.join(batchId, String.format("%05d", Long.parseLong(smsSeq)));
                ps.setString(1, mrSeq);
                ps.setString(2, mobileMessageTo.getObjectNo() == null ? "" : mobileMessageTo.getObjectNo());
                ps.setString(3, mobileMessageTo.getObjectName() == null ? "" : mobileMessageTo.getObjectName());
                ps.setString(4, "101");
                //获取当前日期
                SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
                String currentDay = format.format(new Date());
                ps.setString(5, currentDay);
                ps.setBigDecimal(6, BigDecimal.ZERO);
                ps.setString(7, mobileMessageTo.getMobileNumber());
                ps.setBigDecimal(8, BigDecimal.ZERO);
                ps.setString(9, "N");
                ps.setString(10, mobileMessageTo.getSmsContent());
                ps.setString(11, mobileMessageTo.getSysId() == null ? "" : mobileMessageTo.getSysId());

                ps.addBatch();
            }
            ps.executeBatch();
            connection.commit();
            logger.info("提交完成");
            connection.setAutoCommit(true);
        } catch (Exception e) {
            try {
                //发生异常回滚
                if (!connection.isClosed()) {
                    connection.rollback();
                    connection.setAutoCommit(true);
                }
            } catch (Exception e1) {
                logger.error("回滚事物发送异常:{}", e);
            }
            return null;
        } finally {
            closeConnection(null, ps, connection);
        }
        smsScheduleResult.setRtnCode(MessageRtnCodeEnum.SUCCESS.getRtnCode());
        smsScheduleResult.setRtnMsg(MessageRtnCodeEnum.SUCCESS.getDesc());
        smsScheduleResult.setSuccCount(mobileMessageTos.size());
        smsScheduleResult.setSmsStatus(MessageStatusEnum.SUCCESS.getStatus());
        return smsScheduleResult;
    }

    /**
     * @Auther: qym
     * @Description: 更新交易表状态
     * @Param:
     * @Return:
     * @Date: 2021/1/5 15:22
     */
    private String updateSmsTradeStatus(String batchId, SmsScheduleResult smsScheduleResult) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //获取jdbc连接
            connection = getConnection();
            //关闭自动事物提交
            connection.setAutoCommit(false);
            String updateSql = "update jc_sms_trade set STATUS = ?,SUCC_COUNT = ?,RTN_CODE = ?,RTN_MSG = ? where BATCH_ID = ?";
            ps = (PreparedStatement) connection.prepareStatement(updateSql);
            ps.setString(1, smsScheduleResult.getSmsStatus());
            ps.setInt(2, smsScheduleResult.getSuccCount());
            ps.setString(3, smsScheduleResult.getRtnCode());
            ps.setString(4, smsScheduleResult.getRtnMsg());
            ps.setString(5, batchId);
            ps.executeUpdate();
            connection.commit();
            logger.info("提交完成");
            connection.setAutoCommit(true);
        } catch (Exception e) {
            try {
                //发生异常回滚
                if (!connection.isClosed()) {
                    connection.rollback();
                    connection.setAutoCommit(true);
                }
            } catch (Exception e1) {
                logger.error("回滚事物发送异常:{}", e);
            }
            return null;
        } finally {
            closeConnection(null, ps, connection);
        }
        return MessageRtnCodeEnum.SUCCESS.getRtnCode();
    }

    @Override
    public void init() {
        //发送http请求需要的apollo配置
        comSmsTemplateVar = ParentMessageFactory.getPropertie("sms.http.comSmsTemplateVar");
        comSmsTemplateId = ParentMessageFactory.getPropertie("sms.http.comSmsTemplateId");
        url = ParentMessageFactory.getPropertie("sms.http.url");
        appCode = ParentMessageFactory.getPropertie("sms.http.appCode");
        batchNo = ParentMessageFactory.getPropertie("sms.http.batchNo");
        secretKey = ParentMessageFactory.getPropertie("sms.http.secretKey");
        //定时消息存数据库所需要的apollo配置
        mysqlDriver = ParentMessageFactory.getPropertie("spring.custom.datasource.write.driver-class-name");
        mysqlUrl = ParentMessageFactory.getPropertie("spring.custom.datasource.write.url");
        mysqlUsername = ParentMessageFactory.getPropertie("spring.custom.datasource.write.username");
        mysqlPassword = ParentMessageFactory.getPropertie("spring.custom.datasource.write.password");
    }

}
